package de.hu.wbi.histoner;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
Copyright 2011,2012 Philippe Thomas
This file is part of histoner.

HistoNer is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
any later version.

HistoNer  is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with HistoNer .  If not, see <http://www.gnu.org/licenses/>.
*/


/**
 * Represents a histone - modification; a histone modification consists of 
 * <ul>
 * <li> Histone number (1, 2A, 2B, 3, 3.3, 4, 5)</li>
 * <li> Amino acid (Lysine, Arginine, Serine, Threonine and Glutamate) </li>
 * <li> Position of the amino acid modification </li>
 * <li> The modification itself (methylation, acetylation, etc.) </li>
 * </ul>
 * @author philippe
 * @version 1.0
 *
 */
public class HistoneModification implements Comparable<HistoneModification>{
	
	/**
	 * Represents an amino acid
	 */
	public enum Residue  {K,R,S,T,E};//Lysine, Arginine, Serine, Threonine and Glutamate
	private String histone;	
	private Residue residue;
	private int aaposition;
	private String modification;	
	private String entity; 	//e.g. phosphorylation of histone H3 in Ser(10)
	private String patternId;	//Pattern ID which found this entity
	
	//These two variables define the span of a Entity
	int from;
	int to;

	/**
	 * Creates a histone modification
	 * @param histone Name  of histoe (e.g. 2A or 3)
	 * @param modification Type of modification
	 * @param residue Amino acid residue
	 * @param aaposition Location on amino-acid level
	 * @param from Start position in text
	 * @param to End position in text
	 * @param entity String found in text 
	 */
	public HistoneModification(String histone, String modification, Residue residue, int aaposition, int from, int to, String entity) { 		
		super();
		this.histone = histone;
		this.modification = modification;
		this.residue = residue;
		this.aaposition = aaposition;
		
		if(entity.startsWith(" ") || entity.startsWith("/") ){
			this.from = from -1;
			this.entity = entity.substring(1);
		}
		else{
			this.from = from;
			this.entity = entity;
		}
		
		this.to = to;
		
	}

	/**
	 * Creates a histone modification  from a string following the Brno nomenclature
	 * @param modificationString Histone modification in Brno nomenclature
	 */
	public HistoneModification(String modificationString) {
		super();
		Map<String, Residue> map=residueMapping();
		Pattern pattern = Pattern.compile("(H)(1|2|2A|2AX|2B|2BX|3|3\\.3|4)(K|R|S|T|E)([0-9]+)(me|me1|me2|me3|ac|ac1|ac2|ph|ub1|ubn|ar1|arn|bi)");
		Matcher matcher = pattern.matcher(modificationString);
		
		if(matcher.find() == false){
			throw new RuntimeException("Error parsing " +modificationString );
		}
		
		this.histone = matcher.group(2);
		this.residue = map.get(matcher.group(3));
		this.aaposition = Integer.parseInt(matcher.group(4));
		this.modification = matcher.group(5);
	}



	/**
	 * Mapping from different amino acid descriptors to the FASTA - one letter format 
	 * @return Map<String, Residue> maps strings to one-letter amino.acids
	 */
	public static Map<String, Residue>  residueMapping(){
		
		Map<String, Residue> result = new HashMap<String, Residue>();
		
		result.put("LYSINE", Residue.K);
		result.put("LYS", Residue.K);
		result.put("K", Residue.K);
		
		result.put("ARGININE", Residue.R);
		result.put("ARG", Residue.R);
		result.put("R", Residue.R);
		
		result.put("SERINE", Residue.S);
		result.put("SER", Residue.S);
		result.put("S", Residue.S);
		
		result.put("THREONINE", Residue.T);
		result.put("THR", Residue.T);
		result.put("T", Residue.T);
		
		result.put("GLUTAMIC ACID", Residue.E);
		result.put("GLUTAMATE", Residue.E);
		result.put("GLU", Residue.E);
		result.put("E", Residue.E);		
		
		
		//Amino acids, which are not known to be modified
//		result.put("ALANINE", Residue.A);
//		result.put("ALA", Residue.A);
//		result.put("A", Residue.A);
//   
//		result.put("GLYCINE", Residue.G);
//		result.put("GLY", Residue.G);
//		result.put("G", Residue.G);
//		
//		result.put("LEUCINE", Residue.L);
//		result.put("LEU", Residue.L);
//		result.put("L", Residue.L);
//
//		result.put("METHIONINE", Residue.M);
//		result.put("MET", Residue.M);
//		result.put("M", Residue.M);
//
//		result.put("PHENYLALANINE", Residue.F);
//		result.put("PHE", Residue.F);
//		result.put("F", Residue.F);
//
//		result.put("TRYPTOPHAN", Residue.W);
//		result.put("TRP", Residue.W);
//		result.put("W", Residue.W);
//
//		result.put("GLUTAMINE", Residue.Q);
//		result.put("GLN", Residue.Q);
//		result.put("Q", Residue.Q);
//		
//		result.put("ASPARTATE", Residue.D);
//        result.put("ASPARTIC ACID", Residue.D);
//        result.put("ASP", Residue.D);
//        result.put("D", Residue.D);
//
//		result.put("PROLINE", Residue.P);
//		result.put("PRO", Residue.P);
//		result.put("P", Residue.P);
//
//		result.put("VALINE", Residue.V);
//		result.put("VAL", Residue.V);
//		result.put("V", Residue.V);
//
//		result.put("ISOLEUCINE", Residue.I);
//		result.put("ILE", Residue.I);
//		result.put("I", Residue.I);
//
//        result.put("CYSTEINE", Residue.C);
//        result.put("CYS", Residue.C);
//        result.put("C", Residue.C);
//
//        result.put("TYROSINE", Residue.Y);
//        result.put("TYR", Residue.Y);
//        result.put("Y", Residue.Y);
//
//        result.put("HISTIDINE", Residue.H);
//        result.put("HIS", Residue.H);
////        result.put("H", Residue.H);
//
//        result.put("ASPARAGINE", Residue.N);
//        result.put("ASN", Residue.N);
//        result.put("N", Residue.N);
	
		return result ;
	}

	/**
	 * GBuilds regular expression consisting of all known amino acid terms
	 * @return
	 */
	public static String getResidueRegex(){
		StringBuilder sb = new StringBuilder("");
		Map<String, Residue> residueMap = residueMapping();
		for(String key : residueMap.keySet()){
			if(key.length() > 1)
				sb.append(key.toLowerCase());
			else
				sb.append(key);
			sb.append("|");
		}
		
		sb.delete(sb.length()-1, sb.length());
		return sb.toString();
	}
	
	
	/**
	 * Builds a regular expression string consisting of all different modification terms. E.g. me-?1|me-?2|me-?3|...
	 * @return Regular expression describing all possible modification terms
	 */
	public static String getModificationRegex(){
		StringBuilder sb = new StringBuilder("");
		HashMap<Pattern, String> modifications= (HashMap<Pattern, String>) getModificationMapping();
		List<String> mods = new ArrayList<String>();
		
		for(Pattern p:modifications.keySet()){
			String patternString = p.pattern();
			patternString = patternString.replaceAll("\\(", "\\\\(");
			patternString = patternString.replaceAll("\\)", "\\\\)");
			mods.add(patternString.substring(1, patternString.length()-1));
		}
		
		//Sort modifications by length
		Collections.sort(mods, new StringlengthComparator());
		Collections.reverse(mods);
		for(String mod:mods){
			sb.append(mod +"|");
		}
		sb.delete(sb.length()-1, sb.length());
		
		return sb.toString();
	}
	

	/**
	 * Generates a mapping between different terms for histone modifications (including inflections) to the normalized Brno name
	 * For example 'acetylates' is mapped to 'ac'
	 * @return Map between histone modification terms and the corresponding Brno-normalized modification name
	 */
	static Map<Pattern, String> getModificationMapping(){
		Map<Pattern, String> result = new HashMap<Pattern, String>();
		
		//Acetylations
		result.put(Pattern.compile("^ac$", Pattern.CASE_INSENSITIVE), "ac");
		result.put(Pattern.compile("^ac-?2$",Pattern.CASE_INSENSITIVE), "ac2");

		result.put(Pattern.compile("^\\(ac\\)$", Pattern.CASE_INSENSITIVE), "ac");
		result.put(Pattern.compile("^\\(ac\\)-?2$", Pattern.CASE_INSENSITIVE), "ac2");		
		result.put(Pattern.compile("^ac-?\\(2\\)$", Pattern.CASE_INSENSITIVE), "ac");
		
		result.put(Pattern.compile("^acetylations?$",Pattern.CASE_INSENSITIVE), "ac");			
		result.put(Pattern.compile("^acetylate[s|d]?$",Pattern.CASE_INSENSITIVE), "ac");		
		result.put(Pattern.compile("^acetylating$",Pattern.CASE_INSENSITIVE), "ac");		
		result.put(Pattern.compile("^acetylase[s|d]?$",Pattern.CASE_INSENSITIVE), "ac");	
		result.put(Pattern.compile("^acetyl$", Pattern.CASE_INSENSITIVE), "ac");
				
		result.put(Pattern.compile("^hypo-?acetylations?$", Pattern.CASE_INSENSITIVE), "ac");
		result.put(Pattern.compile("^hypo-?acetylate[s|d]?$", Pattern.CASE_INSENSITIVE), "ac");
		result.put(Pattern.compile("^hypo-?acetylating$", Pattern.CASE_INSENSITIVE), "ac");
		result.put(Pattern.compile("^hypo-?acetylase[s|d]$", Pattern.CASE_INSENSITIVE), "ac");
		
		result.put(Pattern.compile("^hyper-?acetylations?$", Pattern.CASE_INSENSITIVE), "ac");
		result.put(Pattern.compile("^hyper-?acetylate[s|d]?$", Pattern.CASE_INSENSITIVE), "ac");
		result.put(Pattern.compile("^hyper-?acetylating$", Pattern.CASE_INSENSITIVE), "ac");
		result.put(Pattern.compile("^hyper-?acetylase[s|d]$", Pattern.CASE_INSENSITIVE), "ac");
		
		result.put(Pattern.compile("^de-?acetylations?$", Pattern.CASE_INSENSITIVE), "ac");
		result.put(Pattern.compile("^de-?acetylate[s|d]?$", Pattern.CASE_INSENSITIVE), "ac");	
		result.put(Pattern.compile("^de-?acetylating?$", Pattern.CASE_INSENSITIVE), "ac");
		result.put(Pattern.compile("^de-?acetylase[s|d]$", Pattern.CASE_INSENSITIVE), "ac");
		
		result.put(Pattern.compile("^di-?acetylations?$", Pattern.CASE_INSENSITIVE), "ac2");
		result.put(Pattern.compile("^di-?acetylate[s|d]?$", Pattern.CASE_INSENSITIVE), "ac2");
		result.put(Pattern.compile("^di-?acetylating$", Pattern.CASE_INSENSITIVE), "ac2");
		result.put(Pattern.compile("^di-?acetylase[s|d]$", Pattern.CASE_INSENSITIVE), "ac2");
		
		result.put(Pattern.compile("^mono-?acetylations?$", Pattern.CASE_INSENSITIVE), "ac");
		result.put(Pattern.compile("^mono-?acetylate[s|d]?$", Pattern.CASE_INSENSITIVE), "ac");
		result.put(Pattern.compile("^mono-?acetylating$", Pattern.CASE_INSENSITIVE), "ac");
		result.put(Pattern.compile("^mono-?acetylase[s|d]?$", Pattern.CASE_INSENSITIVE), "ac");			
		
		//Methylation
		result.put(Pattern.compile("^me1[/-]2?$", Pattern.CASE_INSENSITIVE), "me1|me2");
		result.put(Pattern.compile("^me2[/-]3?$", Pattern.CASE_INSENSITIVE), "me2|me3");
		result.put(Pattern.compile("^me1[/-]3?$", Pattern.CASE_INSENSITIVE), "me1|me3");
		
		result.put(Pattern.compile("^m$", Pattern.CASE_INSENSITIVE), "me");
		result.put(Pattern.compile("^m1$", Pattern.CASE_INSENSITIVE), "me1");
		result.put(Pattern.compile("^m2$", Pattern.CASE_INSENSITIVE), "me2");
		result.put(Pattern.compile("^m3$", Pattern.CASE_INSENSITIVE), "me3");

		result.put(Pattern.compile("^me$", Pattern.CASE_INSENSITIVE), "me");
		result.put(Pattern.compile("^me-?1$", Pattern.CASE_INSENSITIVE), "me1");
		result.put(Pattern.compile("^me-?2$", Pattern.CASE_INSENSITIVE), "me2");
		result.put(Pattern.compile("^me-?3$", Pattern.CASE_INSENSITIVE), "me3");
						
		result.put(Pattern.compile("^\\(me\\)$", Pattern.CASE_INSENSITIVE), "me");
		result.put(Pattern.compile("^\\(me\\)-?1$", Pattern.CASE_INSENSITIVE), "me1");
		result.put(Pattern.compile("^\\(me\\)-?2$", Pattern.CASE_INSENSITIVE), "me2");
		result.put(Pattern.compile("^\\(me\\)-?3$", Pattern.CASE_INSENSITIVE), "me3");
		
		result.put(Pattern.compile("^me-?\\(1\\)$", Pattern.CASE_INSENSITIVE), "me1");
		result.put(Pattern.compile("^me-?\\(2\\)$", Pattern.CASE_INSENSITIVE), "me2");
		result.put(Pattern.compile("^me-?\\(3\\)$", Pattern.CASE_INSENSITIVE), "me3");		
		
		result.put(Pattern.compile("^me1s$", Pattern.CASE_INSENSITIVE), "me1s");
		result.put(Pattern.compile("^me2s$", Pattern.CASE_INSENSITIVE), "me2s");
		result.put(Pattern.compile("^me3s$", Pattern.CASE_INSENSITIVE), "me3s");

		result.put(Pattern.compile("^me1a$", Pattern.CASE_INSENSITIVE), "me1a");
		result.put(Pattern.compile("^me2a$", Pattern.CASE_INSENSITIVE), "me2a");
		result.put(Pattern.compile("^me3a$", Pattern.CASE_INSENSITIVE), "me3a");			

		result.put(Pattern.compile("^methylations?$", Pattern.CASE_INSENSITIVE), "me");
		result.put(Pattern.compile("^methylate[s|d]?$", Pattern.CASE_INSENSITIVE), "me");
		result.put(Pattern.compile("^methylating$", Pattern.CASE_INSENSITIVE), "me");
		result.put(Pattern.compile("^methylase[s|d]?$", Pattern.CASE_INSENSITIVE), "me");
		result.put(Pattern.compile("^methyl$", Pattern.CASE_INSENSITIVE), "me");
		
		result.put(Pattern.compile("^hypo-?methylations?$", Pattern.CASE_INSENSITIVE), "me");
		result.put(Pattern.compile("^hypo-?methylate[s|d]?$", Pattern.CASE_INSENSITIVE), "me");
		result.put(Pattern.compile("^hypo-?methylating$", Pattern.CASE_INSENSITIVE), "me");
		result.put(Pattern.compile("^hypo-?methylase[s|d]$", Pattern.CASE_INSENSITIVE), "me");
		
		result.put(Pattern.compile("^hyper-?methylations?$", Pattern.CASE_INSENSITIVE), "me");
		result.put(Pattern.compile("^hyper-?methylate[s|d]?$", Pattern.CASE_INSENSITIVE), "me");
		result.put(Pattern.compile("^hyper-?methylating$", Pattern.CASE_INSENSITIVE), "me");
		result.put(Pattern.compile("^hyper-?methylase[s|d]$", Pattern.CASE_INSENSITIVE), "me");
		
		result.put(Pattern.compile("^de-?methylations?$", Pattern.CASE_INSENSITIVE), "me");
		result.put(Pattern.compile("^de-?methylate[s|d]?$", Pattern.CASE_INSENSITIVE), "me");	
		result.put(Pattern.compile("^de-?methylating?$", Pattern.CASE_INSENSITIVE), "me");
		result.put(Pattern.compile("^de-?methylase[s|d]$", Pattern.CASE_INSENSITIVE), "me");

		result.put(Pattern.compile("^mono-?methylation$", Pattern.CASE_INSENSITIVE), "me1");
		result.put(Pattern.compile("^mono-?methylate[s|d]?$", Pattern.CASE_INSENSITIVE), "me1");
		result.put(Pattern.compile("^mono-?methylating$", Pattern.CASE_INSENSITIVE), "me1");
		result.put(Pattern.compile("^mono-?methylases?$", Pattern.CASE_INSENSITIVE), "me1");
		result.put(Pattern.compile("^mono-?methyl$", Pattern.CASE_INSENSITIVE), "me1");
		
		result.put(Pattern.compile("^di-?methylation$", Pattern.CASE_INSENSITIVE), "me2");
		result.put(Pattern.compile("^di-?methylate[s|d]?$", Pattern.CASE_INSENSITIVE), "me2");
		result.put(Pattern.compile("^di-?methylating$", Pattern.CASE_INSENSITIVE), "me2");
		result.put(Pattern.compile("^di-?methylases?$", Pattern.CASE_INSENSITIVE), "me2");
		result.put(Pattern.compile("^di-?methyl$", Pattern.CASE_INSENSITIVE), "me2");
		
		result.put(Pattern.compile("^tri-?methylations?$", Pattern.CASE_INSENSITIVE), "me3");
		result.put(Pattern.compile("^tri-?methylate[s|d]?$", Pattern.CASE_INSENSITIVE), "me3");		
		result.put(Pattern.compile("^tri-?methylases?$", Pattern.CASE_INSENSITIVE), "me3");
		result.put(Pattern.compile("^tri-?methyl$", Pattern.CASE_INSENSITIVE), "me3");
			
		result.put(Pattern.compile("^mono-? (?:and|or) di-?methyl$", Pattern.CASE_INSENSITIVE), "me1|me2");
		result.put(Pattern.compile("^mono-? (?:and|or) di-?methylations?$", Pattern.CASE_INSENSITIVE), "me1|me2");
		result.put(Pattern.compile("^mono-? (?:and|or) di-?methylate[s|d]?$", Pattern.CASE_INSENSITIVE), "me1|me2");
		result.put(Pattern.compile("^di-? (?:and|or) mono-?methyl$", Pattern.CASE_INSENSITIVE), "me2|me1");		
		result.put(Pattern.compile("^di-? (?:and|or) mono-?methylations?$", Pattern.CASE_INSENSITIVE), "me2|me1");
		result.put(Pattern.compile("^di-? (?:and|or) mono-?methylate[s|d]?$", Pattern.CASE_INSENSITIVE), "me2|me1");		
		
		result.put(Pattern.compile("^mono-? (?:and|or) tri-?methyl$", Pattern.CASE_INSENSITIVE), "me1|me3");
		result.put(Pattern.compile("^mono-? (?:and|or) tri-?methylations?$", Pattern.CASE_INSENSITIVE), "me1|me3");
		result.put(Pattern.compile("^mono-? (?:and|or) tri-?methylate[s|d]?$", Pattern.CASE_INSENSITIVE), "me1|me3");
		result.put(Pattern.compile("^tri-? (?:and|or) mono-?methyl$", Pattern.CASE_INSENSITIVE), "me3|me1");
		result.put(Pattern.compile("^tri-? (?:and|or) mono-?methylations?$", Pattern.CASE_INSENSITIVE), "me3|me1");
		result.put(Pattern.compile("^tri-? (?:and|or) mono-?methylate[s|d]?$", Pattern.CASE_INSENSITIVE), "me3|me1");
		
		result.put(Pattern.compile("^di-? (?:and|or) tri-?methyl$", Pattern.CASE_INSENSITIVE), "me2|me3");
		result.put(Pattern.compile("^di-? (?:and|or) tri-?methylations?$", Pattern.CASE_INSENSITIVE), "me2|me3");
		result.put(Pattern.compile("^di-? (?:and|or) tri-?methylate[s|d]?$", Pattern.CASE_INSENSITIVE), "me2|me3");
		result.put(Pattern.compile("^tri-? (?:and|or) di-?methyl$", Pattern.CASE_INSENSITIVE), "me3|me2");
		result.put(Pattern.compile("^tri-? (?:and|or) di-?methylations?$", Pattern.CASE_INSENSITIVE), "me3|me2");
		result.put(Pattern.compile("^tri-? (?:and|or) di-?methylate[s|d]?$", Pattern.CASE_INSENSITIVE), "me2|me3");
		
		result.put(Pattern.compile("^mono-?, di-?, (?:and|or),? tri-?methyl$", Pattern.CASE_INSENSITIVE), "me1|me2|me3");
		result.put(Pattern.compile("^mono-?, di-?, (?:and|or),? tri-?methylations?$", Pattern.CASE_INSENSITIVE), "me1|me2|me3");
		result.put(Pattern.compile("^mono-?, di-?, (?:and|or),? tri-?methylatione[s|d]?$", Pattern.CASE_INSENSITIVE), "me1|me2|me3");
		
//		Phosphorylation
		result.put(Pattern.compile("^ph$", Pattern.CASE_INSENSITIVE), "ph");
		result.put(Pattern.compile("^phosphorylations?$", Pattern.CASE_INSENSITIVE), "ph");
		result.put(Pattern.compile("^phosphorylate[s|d]?$", Pattern.CASE_INSENSITIVE), "ph");
		result.put(Pattern.compile("^phosphorylases?$", Pattern.CASE_INSENSITIVE), "ph");
		result.put(Pattern.compile("^phosphorylating?$", Pattern.CASE_INSENSITIVE), "ph");
		result.put(Pattern.compile("^phosphoprotein?$", Pattern.CASE_INSENSITIVE), "ph");
		result.put(Pattern.compile("^pospho-?$", Pattern.CASE_INSENSITIVE), "ph");
		
//		//Ubiquityl
		result.put(Pattern.compile("^ub$", Pattern.CASE_INSENSITIVE), "ub");
		result.put(Pattern.compile("^ub-?1$", Pattern.CASE_INSENSITIVE), "ub1");
		result.put(Pattern.compile("^\\(ub\\)-?1$", Pattern.CASE_INSENSITIVE), "ub1");
		
		result.put(Pattern.compile("^ubiquitylations?$", Pattern.CASE_INSENSITIVE), "ub");
		result.put(Pattern.compile("^ubiquitylate[s|d]?$", Pattern.CASE_INSENSITIVE), "ub");
		result.put(Pattern.compile("^ubiquitylating$", Pattern.CASE_INSENSITIVE), "ub");
		result.put(Pattern.compile("^ubiquitylases?$", Pattern.CASE_INSENSITIVE), "ub");
		result.put(Pattern.compile("^ubiquityl$", Pattern.CASE_INSENSITIVE), "ub");
		
		result.put(Pattern.compile("^ubiquitinylations?$", Pattern.CASE_INSENSITIVE), "ub");
		result.put(Pattern.compile("^ubiquitinylate[s|d]?$", Pattern.CASE_INSENSITIVE), "ub");
		result.put(Pattern.compile("^ubiquitinylating$", Pattern.CASE_INSENSITIVE), "ub");
		result.put(Pattern.compile("^ubiquitinylases?$", Pattern.CASE_INSENSITIVE), "ub");
		result.put(Pattern.compile("^ubiquitinyl$", Pattern.CASE_INSENSITIVE), "ub");	
		
		result.put(Pattern.compile("^ubiquitinations?$", Pattern.CASE_INSENSITIVE), "ub");
		result.put(Pattern.compile("^ubiquitinate[s|d]?$", Pattern.CASE_INSENSITIVE), "ub");
		result.put(Pattern.compile("^ubiquitinatating$", Pattern.CASE_INSENSITIVE), "ub");
		
		result.put(Pattern.compile("^mono-?ubiquitinylations?$", Pattern.CASE_INSENSITIVE), "ub1");
		result.put(Pattern.compile("^mono-?ubiquitinylate[s|d]?$", Pattern.CASE_INSENSITIVE), "ub1");
		result.put(Pattern.compile("^mono-?ubiquitinylating$", Pattern.CASE_INSENSITIVE), "ub1");
		result.put(Pattern.compile("^mono-?ubiquitinylases?$", Pattern.CASE_INSENSITIVE), "ub1");
		result.put(Pattern.compile("^mono-?ubiquitinyl$", Pattern.CASE_INSENSITIVE), "ub1");

		result.put(Pattern.compile("^mono-?ubiquitylations?$", Pattern.CASE_INSENSITIVE), "ub1");
		result.put(Pattern.compile("^mono-?ubiquitylate[s|d]?$", Pattern.CASE_INSENSITIVE), "ub1");
		result.put(Pattern.compile("^mono-?ubiquitylating$", Pattern.CASE_INSENSITIVE), "ub1");
		result.put(Pattern.compile("^mono-?ubiquitylases?$", Pattern.CASE_INSENSITIVE), "ub1");
		result.put(Pattern.compile("^mono-?ubiquityl$", Pattern.CASE_INSENSITIVE), "ub1");
		
		result.put(Pattern.compile("^mono-?ubiquitinalate[s|d]?$", Pattern.CASE_INSENSITIVE), "ub1");
		result.put(Pattern.compile("^mono-?ubiquitinalating$", Pattern.CASE_INSENSITIVE), "ub1");
		result.put(Pattern.compile("^mono-?ubiquitinales?$", Pattern.CASE_INSENSITIVE), "ub1");
		result.put(Pattern.compile("^mono-?ubiquitinal?$", Pattern.CASE_INSENSITIVE), "ub1");
		
		result.put(Pattern.compile("^mono-?ubiquitinations?$", Pattern.CASE_INSENSITIVE), "ub1");
		result.put(Pattern.compile("^mono-?ubiquitinate[s|d]?$", Pattern.CASE_INSENSITIVE), "ub1");
		result.put(Pattern.compile("^mono-?ubiquitinating$", Pattern.CASE_INSENSITIVE), "ub1");

		result.put(Pattern.compile("^poly-?ubiquitinylations??$", Pattern.CASE_INSENSITIVE), "ubn");
		result.put(Pattern.compile("^poly-?ubiquitinylate[s|d]?$", Pattern.CASE_INSENSITIVE), "ubn");
		result.put(Pattern.compile("^poly-?ubiquitinylating$", Pattern.CASE_INSENSITIVE), "ubn");
		result.put(Pattern.compile("^poly-?ubiquitinylases?$", Pattern.CASE_INSENSITIVE), "ubn");
		result.put(Pattern.compile("^poly-?ubiquitinyl$", Pattern.CASE_INSENSITIVE), "ubn");

		result.put(Pattern.compile("^poly-?ubiquitinations?$", Pattern.CASE_INSENSITIVE), "ubn");
		result.put(Pattern.compile("^poly-?ubiquitinalate[s|d]?$", Pattern.CASE_INSENSITIVE), "ubn");
		result.put(Pattern.compile("^poly-?ubiquitinalating$", Pattern.CASE_INSENSITIVE), "ubn");
		result.put(Pattern.compile("^poly-?ubiquitinales?$", Pattern.CASE_INSENSITIVE), "ubn");
		result.put(Pattern.compile("^poly-?ubiquitinal?$", Pattern.CASE_INSENSITIVE), "ubn");
		
		result.put(Pattern.compile("^poly-?ubiquitylations?$", Pattern.CASE_INSENSITIVE), "ubn");
		result.put(Pattern.compile("^poly-?ubiquitylate[s|d]?$", Pattern.CASE_INSENSITIVE), "ubn");
		result.put(Pattern.compile("^poly-?ubiquitylating$", Pattern.CASE_INSENSITIVE), "ubn");
		result.put(Pattern.compile("^poly-?ubiquitylases?$", Pattern.CASE_INSENSITIVE), "ubn");
		result.put(Pattern.compile("^poly-?ubiquityl$", Pattern.CASE_INSENSITIVE), "ubn");
		
		result.put(Pattern.compile("^poly-?ubiquitination?$", Pattern.CASE_INSENSITIVE), "ubn");
		result.put(Pattern.compile("^poly-?ubiquitinate[s|d]?$", Pattern.CASE_INSENSITIVE), "ubn");
		result.put(Pattern.compile("^poly-?ubiquitinating$", Pattern.CASE_INSENSITIVE), "ubn");
		
		//Sumoylation
		result.put(Pattern.compile("^su$", Pattern.CASE_INSENSITIVE), "su");
		result.put(Pattern.compile("^sumoylations?$",Pattern.CASE_INSENSITIVE), "su");			//Noun
		result.put(Pattern.compile("^sumoylate[s|d]?$",Pattern.CASE_INSENSITIVE), "su");		//Verb (acetylate, acetylates, acetylated)
		result.put(Pattern.compile("^sumoylating$",Pattern.CASE_INSENSITIVE), "su");		//Verb (acetylate, acetylates, acetylated)
		result.put(Pattern.compile("^sumoylase[s|d]?$",Pattern.CASE_INSENSITIVE), "su");			//
		result.put(Pattern.compile("^sumoyl$", Pattern.CASE_INSENSITIVE), "su");
		
		//ADP-ribosyl
		result.put(Pattern.compile("^ar$", Pattern.CASE_INSENSITIVE), "ar");	//Negative look-behind assertion, needed that 1|2|3ac can still "hit"
		result.put(Pattern.compile("^ar-?1$", Pattern.CASE_INSENSITIVE), "ar1");
		result.put(Pattern.compile("^ar-?n$", Pattern.CASE_INSENSITIVE), "arn");
		
		result.put(Pattern.compile("^adp-?ribosylations?$",Pattern.CASE_INSENSITIVE), "ar1");			//Noun
		result.put(Pattern.compile("^adp-?ribosylate[s|d]?$",Pattern.CASE_INSENSITIVE), "ar1");		//Verb (acetylate, acetylates, acetylated)
		result.put(Pattern.compile("^adp-?ribosylating$",Pattern.CASE_INSENSITIVE), "ar1");		//Verb (acetylate, acetylates, acetylated)
		result.put(Pattern.compile("^adp-?ribosylase[s|d]?$",Pattern.CASE_INSENSITIVE), "ar1");			//
		
		result.put(Pattern.compile("^poly-?ribosylations?$",Pattern.CASE_INSENSITIVE), "arn");			//Noun
		result.put(Pattern.compile("^poly-?ribosylate[s|d]?$",Pattern.CASE_INSENSITIVE), "arn");		//Verb (acetylate, acetylates, acetylated)
		result.put(Pattern.compile("^poly-?ribosylating$",Pattern.CASE_INSENSITIVE), "arn");		//Verb (acetylate, acetylates, acetylated)
		result.put(Pattern.compile("^poly-?ribosylase[s|d]?$",Pattern.CASE_INSENSITIVE), "arn");			//
		
		//Biotinylation
		result.put(Pattern.compile("^biotinylations?$",Pattern.CASE_INSENSITIVE), "bi");			//Noun
		result.put(Pattern.compile("^biotinylate[s|d]?$",Pattern.CASE_INSENSITIVE), "bi");		//Verb (acetylate, acetylates, acetylated)
		result.put(Pattern.compile("^biotinylating$",Pattern.CASE_INSENSITIVE), "bi");		//Verb (acetylate, acetylates, acetylated)
		result.put(Pattern.compile("^biotinylase[s|d]?$",Pattern.CASE_INSENSITIVE), "bi");			//
		
		return result;		
	}
	
	/**
	 * Checks if two histone modifications do overlap. This might happen for two reasons:
	 * <ul>
	 * <li>The phrase describes more than one modification</li>
	 * <li>The phrase describes only one modification, but is hit by several regular expressions -> Remove such hits</li>
	 * </ul>
	 *  
	 * @param  other Other histone modification to compare with
	 * @return boolean True if the two modifications are overlapping
	 */
	public boolean overlaps(HistoneModification other){
		if(this.from <= other.from && this.to >= other.from)
			return true;
		
		if(this.from >= other.from && this.from <= other.to)
			return true;

		return false;
	}
	
	@Override
	/**
	 * @return result Returns hash code
	 */
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + aaposition;
		result = prime * result + ((histone == null) ? 0 : histone.hashCode());
		result = prime * result
				+ ((modification == null) ? 0 : modification.hashCode());
		result = prime * result + ((residue == null) ? 0 : residue.hashCode());
		result = prime * result + from;
		result = prime * result + to;
		return result;
	}


	@Override
	/**
	 * @return boolean Check if two histone modifications are equal.
	 */
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		
		if (obj == null)
			return false;
		
		if (getClass() != obj.getClass())
			return false;
		
		HistoneModification other = (HistoneModification) obj;
		
		if (aaposition != other.aaposition)
			return false;
		
		if (from != other.from)
			return false;
		
		if (histone == null) {
			if (other.histone != null)
				return false;
		} else if (!histone.equals(other.histone))
			return false;
		if (modification == null) {
			if (other.modification != null)
				return false;
		} else if (!modification.equals(other.modification))
			return false;
		if (residue == null) {
			if (other.residue != null)
				return false;
		} else if (!residue.equals(other.residue))
			return false;
		if (to != other.to)
			return false;
		return true;
	}

	@Override
	/**
	 * @return String Returns a string in Brno-nomenclature with start and stop position
	 */
	public String toString() {
		return this.toBrnoString() +"\t" +from +"-" +to;
	}
	
	/**
	 * 
	 * @return String Returns a string in Brno nomenclature
	 */
	public String toBrnoString() {
		return "H" +histone +residue +aaposition +modification;
	}

	@Override
	/**
	 * Compares two histone modifications based on the string location
	 */
	public int compareTo(HistoneModification o) {
		
		if(this.equals(o))
			return 0;
		
		if (this.from -o.from != 0)
			return this.from -o.from;
		
		if(this.from -o.from +this.to -o.to != 0)
			return this.from -o.from +this.to -o.to;
				
		if (this.toString().compareTo(o.toString()) == 0)
			System.err.println("Should not happen");
		
		return 
			this.toString().compareTo(o.toString());
	}
	
	/**
	 * 
	 * @return Returns the name of the histone
	 */
	public String getHistone() {
		return histone;
	}

	/**
	 * 
	 * @return Returns the modification 
	 */
	public String getModification() {
		return modification;
	}

	/**
	 * 
	 * @return Returns the amino acid one letter  (e.g. L, R, ..)
	 */
	public Residue getResidue() {
		return residue;
	}

	/**
	 * 
	 * @return Returns the amino acid location
	 */
	public int getAaposition() {
		return aaposition;
	}

	/**
	 * 
	 * @return Returns the start position of the modification
	 */
	public int getFrom() {
		return from;
	}

	/**
	 * 
	 * @return Returns the stop position of the modification
	 */
	public int getTo() {
		return to;
	}
	
	/**
	 * 
	 * @return Returns the matched string (e.g. 'di-methylation of histone H3 lysine 27')
	 */
	public String getEntity() {
		return entity;
	}
	
	/**
	 * 
	 * @return Returns the responsible patternId for this String
	 */
	public String getPatternId(){
		return patternId;
	}
	
	/**
	 * 
	 * @param patternId Sets the responsible patternId
	 */
	public void setPatternId(String patternId){
		this.patternId = patternId;
	}
	
	/**
	 * According to the BRNO Nomenclature only histone residues K,R,S,T,E can
	 * be modified. So this expression checks if one of the other amino acids has been found
	 * @return
	 */
	public boolean isRegularAA(){
		
		if(this.residue == Residue.K || this.residue == Residue.R || this.residue == Residue.S || this.residue == Residue.T || this.residue == Residue.E)
			return true;
		
		return false;
	}
}